package com.meiyuetao.myt.core;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.JoinColumn;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;

import lab.s2jh.core.annotation.MetaData;
import lab.s2jh.core.entity.BaseEntity;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.hibernate.annotations.Formula;
import org.hibernate.cfg.ImprovedNamingStrategy;
import org.springframework.util.Assert;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

public class EntityCommentsGenerator {

    public static void main(String[] args) throws Exception {
        String pkg = "com.meiyuetao";
        List list = null;
        // list = getClassList(pkg, recursive, null);
        // 增加 author.class的过滤项，即可只选出ClassTestDemo
        list = getClassList(pkg, true, Table.class);

        for (int i = 0; i < list.size(); i++) {
            Class clazz = (Class) list.get(i);
            Table table = (Table) clazz.getAnnotation(Table.class);
            String tblName = table.name();
            if ("myt_biz_trade_unit".equalsIgnoreCase(tblName)) {
                continue;
            }
            System.out.println("--" + i + ":" + clazz + ":" + tblName);
            Assert.notNull(tblName);
            MetaData tableMetaData = (MetaData) clazz.getAnnotation(MetaData.class);
            if (tableMetaData != null) {
                String tblCommnents = tableMetaData.value();
                StringBuilder sb = new StringBuilder();
                sb.append("IF ((SELECT COUNT(*) from fn_listextendedproperty('MS_Description',  'SCHEMA', N'dbo',  'TABLE', N'" + tblName + "', NULL, NULL)) = 0)  ");
                sb.append("EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'" + tblCommnents + "' ");
                sb.append(", @level0type = 'SCHEMA', @level0name = N'dbo' , @level1type = 'TABLE', @level1name = N'" + tblName + "';");
                System.out.println(sb);
            }

            Class loopClass = clazz;
            Set<Field> fields = Sets.newHashSet(loopClass.getDeclaredFields());
            loopClass = loopClass.getSuperclass();
            while (!loopClass.equals(BaseEntity.class) && !loopClass.equals(Object.class)) {
                fields.addAll(Sets.newHashSet(loopClass.getDeclaredFields()));
                loopClass = loopClass.getSuperclass();
            }

            ImprovedNamingStrategy ins = new ImprovedNamingStrategy();

            for (Field field : fields) {
                if (Modifier.isStatic(field.getModifiers()) || !Modifier.isPrivate(field.getModifiers()) || Collection.class.isAssignableFrom(field.getType())) {
                    continue;
                }
                String name = field.getName();
                System.out.println("--field name: " + name);
                if ("id".equals(name)) {
                    continue;
                }
                MetaData metaData = (MetaData) field.getAnnotation(MetaData.class);
                if (metaData != null) {
                    Class fieldType = field.getType();
                    String fieldName = null;
                    Method method = MethodUtils.getAccessibleMethod(clazz, "get" + StringUtils.capitalize(name), null);
                    Transient transientAnn = method.getAnnotation(Transient.class);
                    if (transientAnn != null) {
                        continue;
                    }
                    Formula formulaAnn = method.getAnnotation(Formula.class);
                    if (formulaAnn != null) {
                        continue;
                    }

                    JoinColumn joinColumn = method.getAnnotation(JoinColumn.class);
                    if (joinColumn != null) {
                        fieldName = joinColumn.name();
                    } else {
                        Column column = method.getAnnotation(Column.class);
                        if (column != null && StringUtils.isNotBlank(column.name())) {
                            fieldName = column.name();
                        } else {
                            PrimaryKeyJoinColumn primaryKeyJoinColumn = method.getAnnotation(PrimaryKeyJoinColumn.class);
                            if (primaryKeyJoinColumn != null) {
                                continue;
                            }
                            fieldName = ins.propertyToColumnName(name);
                        }
                    }

                    String comments = metaData.value();
                    if (fieldType.isEnum()) {
                        List<String> enums = Lists.newArrayList();
                        Field[] fieldEnum = fieldType.getFields();
                        for (Field fe : fieldEnum) {
                            String enumName = fe.getName();
                            MetaData metaDataEnum = (MetaData) fe.getAnnotation(MetaData.class);
                            if (metaDataEnum != null) {
                                enumName = enumName + "=" + metaDataEnum.value();
                            }
                            enums.add(enumName);
                        }
                        comments += "[" + StringUtils.join(enums, ",") + "]";
                    }
                    comments = StringUtils.remove(comments, "'");
                    StringBuilder sb = new StringBuilder();
                    sb.append("IF ((SELECT COUNT(*) from fn_listextendedproperty('MS_Description',  'SCHEMA', N'dbo',  'TABLE', N'" + tblName + "', 'COLUMN', N'" + fieldName
                            + "')) = 0)  ");
                    sb.append("EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'" + comments + "' ");
                    sb.append(", @level0type = 'SCHEMA', @level0name = N'dbo' , @level1type = 'TABLE', @level1name = N'" + tblName + "', @level2type = 'COLUMN', @level2name = N'"
                            + fieldName + "';");
                    System.out.println(sb);
                }
            }
        }
        System.out.println("--done");
    }

    public static List<Class<?>> getClassList(String pkgName, boolean isRecursive, Class<? extends Annotation> annotation) {
        List<Class<?>> classList = new ArrayList<Class<?>>();
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        try {
            // 按文件的形式去查找
            String strFile = pkgName.replaceAll("\\.", "/");
            Enumeration<URL> urls = loader.getResources(strFile);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (url != null) {
                    String protocol = url.getProtocol();
                    String pkgPath = url.getPath();
                    // System.out.println("protocol:" + protocol + " path:" +
                    // pkgPath);
                    if ("file".equals(protocol)) {
                        // 本地自己可见的代码
                        findClassName(classList, pkgName, pkgPath, isRecursive, annotation);
                    } else if ("jar".equals(protocol)) {
                        // 引用第三方jar的代码
                        // findClassName(classList, pkgName, url, isRecursive,
                        // annotation);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classList;
    }

    public static void findClassName(List<Class<?>> clazzList, String pkgName, String pkgPath, boolean isRecursive, Class<? extends Annotation> annotation) {
        if (clazzList == null) {
            return;
        }
        File[] files = filterClassFiles(pkgPath);// 过滤出.class文件及文件夹
        // System.out.println("files:" + ((files == null) ? "null" : "length=" +
        // files.length));
        if (files != null) {
            for (File f : files) {
                String fileName = f.getName();
                if (f.isFile()) {
                    // .class 文件的情况
                    String clazzName = getClassName(pkgName, fileName);
                    addClassName(clazzList, clazzName, annotation);
                } else {
                    // 文件夹的情况
                    if (isRecursive) {
                        // 需要继续查找该文件夹/包名下的类
                        String subPkgName = pkgName + "." + fileName;
                        String subPkgPath = pkgPath + "/" + fileName;
                        findClassName(clazzList, subPkgName, subPkgPath, true, annotation);
                    }
                }
            }
        }
    }

    private static File[] filterClassFiles(String pkgPath) {
        if (pkgPath == null) {
            return null;
        }
        // 接收 .class 文件 或 类文件夹
        return new File(pkgPath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });
    }

    private static String getClassName(String pkgName, String fileName) {
        int endIndex = fileName.lastIndexOf(".");
        String clazz = null;
        if (endIndex >= 0) {
            clazz = fileName.substring(0, endIndex);
        }
        String clazzName = null;
        if (clazz != null) {
            clazzName = pkgName + "." + clazz;
        }
        return clazzName;
    }

    private static void addClassName(List<Class<?>> clazzList, String clazzName, Class<? extends Annotation> annotation) {
        if (clazzList != null && clazzName != null) {
            Class<?> clazz = null;
            try {
                clazz = Class.forName(clazzName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            // System.out.println("isAnnotation=" + clazz.isAnnotation()
            // +" author:" + clazz.isAnnotationPresent(author.class));

            if (clazz != null) {
                if (annotation == null) {
                    clazzList.add(clazz);
                    // System.out.println("add:" + clazz);
                } else if (clazz.isAnnotationPresent(annotation)) {
                    clazzList.add(clazz);
                    // System.out.println("add annotation:" + clazz);
                }
            }
        }
    }
}
